#include <stdio.h>
#include <string.h>
#ifndef _MSC_VER
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#define closesocket close
#endif   // !_MSC_VER
#include <openssl/ssl.h>
#include <openssl/err.h>

#define PORT 18888
#define SERVER_IP "127.0.0.1"
#define BUFFER_SIZE 1024

SSL_CTX *create_ssl_context() {
    SSL_CTX *ctx = NULL;

    // 使用 TLS 客户端方法（兼容 OpenSSL 3.x）
#if OPENSSL_VERSION_NUMBER < 0x1010000fL
    ctx = SSL_CTX_new(SSLv23_client_method());
#else
    ctx = SSL_CTX_new(TLS_client_method());
#endif
    if (!ctx) {
        ERR_print_errors_fp(stderr);
        return NULL;
    }

    // 强制使用 TLS 1.2
#if OPENSSL_VERSION_NUMBER < 0x1010000fL
    SSL_CTX_set_options(ctx,                   SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1);
#else
    SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION);
    SSL_CTX_set_max_proto_version(ctx, TLS1_2_VERSION);
#endif

    // ---------- 关闭证书校验（不安全！仅测试用） ----------
    SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL); // 不验证服务端证书
#ifdef SSL_OP_IGNORE_UNEXPECTED_EOF
    SSL_CTX_set_options(ctx, SSL_OP_IGNORE_UNEXPECTED_EOF); // 兼容性选项
#endif

    return ctx;
}

int wait_data(int fd, unsigned int ms)
{
    int result;
reselect:
    fd_set fds;
    FD_ZERO(&fds);
    FD_SET(fd, &fds);

    struct timeval timeout;
    timeout.tv_sec  = ms / 1000;
    timeout.tv_usec = (ms % 1000) * 1000;

    result = select(fd + 1, &fds, NULL, NULL, &timeout);
    if (result < 0)
    {
        if (errno == EINTR)
        {
            printf("select() call was interrupted, repeating\n");
            goto reselect;
        }
        else
        {
            //printf("select() failed\n");
        }
    }
    else if (result == 0)
    {
        //rintf("Timeout has expired.\n");
    }
    return result;
}

int main() {
#ifdef _MSC_VER
    WSADATA data;
    WORD    version = MAKEWORD(2, 2);

    WSAStartup(version, &data);
#endif
    SSL_CTX *ctx;
    SSL *ssl;
    int sockfd;
    struct sockaddr_in server_addr;
    char buffer[BUFFER_SIZE];

    // 初始化 OpenSSL 3.x
#if OPENSSL_VERSION_NUMBER < 0x1010000fL
    (void)SSL_library_init();
    SSL_load_error_strings();
#else    
    OPENSSL_init_ssl(0, NULL);
#endif

    ctx = create_ssl_context();
    if (!ctx) exit(EXIT_FAILURE);

    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(PORT);
    inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr);

    connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr));

    ssl = SSL_new(ctx);
    SSL_set_fd(ssl, sockfd);

    if (SSL_connect(ssl) <= 0) {
        ERR_print_errors_fp(stderr);
    } else {
        SSL_write(ssl, "Hello from TLS Client!", strlen("Hello from TLS Client!"));

re_wait:
        int result = wait_data(sockfd, 1000);
        if (result == 0)
        {
            printf("Timeout has expired.\n");
            goto re_wait;
        }
        else if (result < 0)
        {
            printf("select() failed, %d\n", result);
            exit(EXIT_FAILURE);
        }
        printf("result = %d\n", result);

        int bytes = SSL_read(ssl, buffer, sizeof(buffer));
        buffer[bytes] = '\0';
        printf("Received: %s\n", buffer);
    }

    SSL_shutdown(ssl);
    SSL_free(ssl);
    SSL_CTX_free(ctx);
    closesocket(sockfd);

    return 0;
}
